Declare Statement

Used to make API calls. Both PPC and x86 machines are supported.

DLLs can be called from a built Windows application. Shared libraries can be called from Macintosh and Linux applications.


Syntax

[Soft] Declare Sub/Function Name Lib LibraryName [Alias AliasName] ([Parameters]) [As ReturnType]

PartTypeDescription
Name Name Name of the API call. Use Name in your code to refer to the API call.
LibraryName String Library in which API call is found.
AliasName String Optional: If the API call has the same name as a REALbasic method, declare the call with a different name and use the alias to refer to the actual API call.
Parameters Parameters of the API call. You can pass Nil to a parameter of type Ptr.
ReturnType Optional: The data type of the value returned by the call if it is a function.


Notes

Function names for the OS are case sensitive. For example, SetLocalTime works on Win32 but SetlocalTime does not.

Declare statements must not call strictly "classic" libraries like AppearanceLib to run on Mac OS X. If they do not, the application will run in the "classic" environment within Mac OS X.

The Declare statement allows use of constants for Lib names.

When building a Carbon application in the Mach-O format, the Lib parameter to a Declare statement now accepts the name of a system framework in place of a full path to the library. If you wrote a declare against "CoreMIDI", for example, the compiler would see that the Lib string did not contain any path separators, and would expand it to "/System/Library/Frameworks/CoreMIDI.framework/CoreMIDI".

You can pass Nil to a Declare parameter of type Ptr. Passing Nil is not the same as passing a Nil MemoryBlock. The former is a constant and the latter require a conversion operation at run time. If the MemoryBlock is Nil, then the conversion operator will generate a NilObjectException. However, this isn't a problem when passing the constant Nil, as no conversion is needed.

Soft Declares

In versions of REALbasic prior to 2005, the only supported type of declares was a "hard" declare. A hard declare of the form:

Declare Sub Foo Lib "Bar" ()

tells REALbasic that Foo will always be in Bar, and Bar will always be installed on the user's system. When building your application, REALbasic puts this dependency in your application's Import table. When the system loader tries to load your application, it will automatically find all of these Declares and try to resolve them. If it can't resolve a library or a function from a library, your application would refuse to load and terminate.

The 'Soft" Declare keyword provides some important flexibility. Soft declares are not put into your application's import table so the system loader doesn't try to resolve them. Instead, when you first try to use the softly declared method, REALbasic is responsible for resolving the library and functions. This provides you with a few handy features.

First, it means that you now have a graceful way to deal with the situation of a function or library that isn't found. Instead of your application just not launching, you now have a FunctionNotFound exception that you can catch. Second, REALbasic can be more flexible about loading the library.

For example, LibC on many newer Linux distributions is really a ld loader script that GCC uses to determine which version of LibC to actually link against. This means that you can't write hard declares that link against LibC in REALbasic because libc.so isn't really a symbolic link to the proper shared library (like most other .so files are). You would have to write a Declare against the actual LibC version you wanted, which provides for a very poor experience for users who don't have the same version of LibC installed as you. However, with a soft declare, REALbasic can resolve the loader script for LibC and find the proper shared library to load against In other words, what used to be declared like this:

Declare Function getpid Lib "libc-2.3.2.so" () as Integer

Can now be declared like this:

Soft Declare Function getpid Lib "LibC" () as Integer

With the Soft keyword, you don't have to try to link against a specific version of that library.

On Windows, any function that uses a string will have two different versions, the A version, and the W version. This is the Win32 way of dealing with Unicode. The disadvantage is that the W version of the method is usually available only on Windows NT machines, not 9x ones. If you could not use a Soft declare, you'd have to make two different versions of your application. Using Soft declares, you can do this:

Soft Declare Function MessageBoxA Lib "User32" ( hwnd as Integer, _
                 msg as CString, title as CString, flags as Integer ) as Integer
SoftInteger, _
                 msg as WString, title as WString, flags as Integer ) as Integer
Try
  Call MessageBoxW( hWnd, "This is a string that handles unicode", _
                "Title here", 0 )
Catch
  Call MessageBoxA( hWnd, "This is a string that doesn't handle unicode", _
                "Title here", 0 )
End

This code runs on any version of Windows. The reason is simple: if the wide char version (W version) is available, then your call will not throw an exception, and because of the WString data type, all your strings will be automatically converted into UTF-16 strings when passed into the function. However, if the call doesn't resolve because you're running on Windows 98 (or another 9x machine), then the exception will be thrown and will use the ANSI (A version) of the function call.

Here is a feature of Soft declares for Macintosh builds. You can call into Mach-O bundles from within a CFM application. For example, if you wanted to use the BSD function called "socket", you can use a soft declare like this:

Soft Declare Function socket Lib "System.framework" ( domain as Integer,type as Integer, protocol as Integer ) as Integer

Checking that a Function is Available

To check whether a function can be resolved, you can use the IsFunctionAvailable function of the System object. It determines whether a function can be resolved without having to actually declare it and try to call into it.

Data Types

The following data types are for use with Declares only:

TypeDescription
Byte Signed 8-bit integer
CFStringRef A CFString reference
CString A null-terminated string.
OSType A four char code
PString A "Pascal" string.
Ptr 4-byte pointer to memory
Short 2-byte signed integer
UByte Unsigned 8-bit integer
UShort Unsigned 2-byte integer
WindowPtr 4-byte integer to a handle
WString A UTF-16 string.

See the descriptions of each data type for more information.

Automatic Types

There are two new automatic types: WString and CFStringRef. You can pass Nil in place of a CString, WString, CFStringRef, or PString.

You can use these two new types with declares that need a wchar_t * or CFStringRef, respectively. Their usage is the same as with CString, and all data is cleaned up when appropriate. For example:

Declare Sub Foo Lib "Bar"(utf16Str as WString)

Examples

Name Conflicts

Suppose you want to write a function that got the various system metrics. You use a method declaration like this:

Function GetSystemMetrics (whichMetric as Integer) as Integer

However, this conflicts with the Win32 API which the function wraps. In this case, you can rename the declare using an alias such as this:

Declare Function MyGetSystemMetrics Lib "User32" Alias "GetSystemMetrics" _
                             type as Integer ) as Integer

Defining Double-clicks

The following example displays the maximum number of ticks between mouse clicks for two clicks to be considered a "double-click." Above this value, two clicks are considered separate mouse events.

Dim DoubleClickTime as Integer
#if TargetMacOS
Declare Function GetDblTime Lib "InterfaceLib" () as Integer
doubleClickTime = GetDblTime()
#endif

#if TargetWin32
Declare Function GetDoubleClickTime Lib "User32.DLL" () as Integer
doubleClickTime = GetDoubleClickTime()
#endif
MsgBox Str(doubleclicktime)

To modify the code to run under Carbon, substitute "CarbonLib" for "InterfaceLib":

Dim DoubleClickTime as Integer
#if TargetCarbon
Declare Function GetDblTime Lib "CarbonLib" () as Integer
doubleClickTime = GetDblTime()
#endif
MsgBox Str(doubleclicktime)

The following example gets the current process ID and illustrates Declares for Mach-O, Mac 'classic', Windows, and Linux.

Function GetPid() As Integer
  Dim pid as Integer
  Dim psn as MemoryBlock

 #if TargetWin32
  ' On Windows, we're going to use the GetCurrentProcessId
  ' function to figure out our pid
  DeclareInteger
  ' Get the pid according to windows
  id = GetCurrentProcessId

 #elseif TargetMachO
  ' For a Mach-O app, we can just use the system's
  'getpid function in the kernel framework, which
  ' is automatically included in the system framework.
  Declare_
() as Integer
  ' Get the pid according to mach
  pid = getpid
 #elseif TargetLinux
  ' On Linux, we get the pid from the libc library.  It
  ' is worth noting that I am hard coding the version
  ' of libc in this call.  It requires libc version 2.3.2
  DeclareInteger
  ' Get the pid according to the kernel
  pid = getpid

 #elseif TargetCarbon or TargetMacOSClassic
  ' On Carbon PEF or Classic we will use the lower
  ' 32 bits of the ProcessSerialNumber.
  DeclareInteger

   ' Make a 64 bit structure
  psn = New MemoryBlock( 8 )
   ' Get the PSN from it
   Call GetCurrentProcess( psn )
   ' And store off the lower 32 bits (the upper
   ' 32 seemed to always be 0 anyways)
  pid = psn.Long( 4 )
 #endif

  ' Return the pid we found (note, if we didn't
  ' find a pid, it'll return 0)
  Return pid

The following example adds the ability to detect a double-click to a Canvas control. The code is placed in a Canvas's MouseUp event handler.

Sub MouseUp (X as Integer, Y as Integer)
Dim doubleClickTime, currentClickTicks as Integer

 #If TargetMacOS then
  #if TargetCarbon then
   Declare Function GetDblTime Lib "CarbonLib" () as Integer
 #endif
  DoubleClickTime = GetDblTime()
 #endif
  
 #if TargetWin32 then
  Declare Function GetDoubleClickTime Lib "User32.DLL" () as Integer
  doubleClickTime = GetDoubleClickTime()
 #endif
  
 #if TargetLinux then
  Declare Function gtk_settings_get_default lib "libgtk-x11-2.0.so" as Ptr
  Declare Sub g_object_get lib "libgtk-x11-2.0.so" (Obj as Ptr, _
    first_property_name as CString, byref doubleClicktime as Integer, _
    Null as Integer)

   Dim gtkSettings as MemoryBlock
    
  gtkSettings = gtk_settings_get_default()
    
  g_object_get(gtkSettings,"gtk-double-click-time",doubleClickTime, 0)
    // DoubleClickTime now holds the number of milliseconds
  DoubleClickTime = DoubleClickTime / 1000.0 * 60
 #endif
  
 currentClickTicks = ticks
 //if the two clicks happened close enough together in time
 if (currentClickTicks - lastClickTicks) <= doubleClickTime then
//if the two clicks occured close enough together in space
  If Abs(X - lastClickX) <= 5 And Abs(Y - LastClickY) <= 5 then
   DoubleClick //a double click has occured so call the event
  end if
 end if
 lastClickTicks = currentClickTicks
 lastClickX = X
 lastClickY = Y

See Also

TargetCarbon, TargetLinux, TargetMacOS, TargetMacOSClassic, TargetMachO, TargetWin32 constants; System object, FunctionNotFound exception; Byte, CFStringRef, CString, OSType, PString, Ptr, Short, UByte, UShort, WindowPtr, WString data types.